//! Error handling for the AI Test Platform
//! 
//! This module provides a comprehensive error handling system that covers
//! all possible error scenarios in the platform.

use std::fmt;
use thiserror::Error;

/// Main error type for the AI Test Platform
#[derive(Error, Debug)]
pub enum Error {
    /// Configuration errors
    #[error("Configuration error: {0}")]
    Config(#[from] ConfigError),
    
    /// Actor system errors
    #[error("Actor error: {0}")]
    Actor(#[from] ActorError),
    
    /// Test execution errors
    #[error("Test execution error: {0}")]
    TestExecution(#[from] TestExecutionError),
    
    /// Plugin errors
    #[error("Plugin error: {0}")]
    Plugin(#[from] PluginError),
    
    /// Storage errors
    #[error("Storage error: {0}")]
    Storage(#[from] StorageError),
    
    /// Network/HTTP errors
    #[error("Network error: {0}")]
    Network(#[from] NetworkError),

    /// Database-related errors
    #[error("Database error: {0}")]
    Database(#[from] DatabaseError),

    /// Cache-related errors
    #[error("Cache error: {0}")]
    Cache(#[from] CacheError),
    
    /// AI service errors
    #[error("AI service error: {0}")]
    AI(#[from] AIError),
    
    /// Validation errors
    #[error("Validation error: {0}")]
    Validation(#[from] ValidationError),
    
    /// Serialization/Deserialization errors
    #[error("Serialization error: {0}")]
    Serialization(#[from] SerializationError),
    
    /// IO errors
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    /// Generic errors
    #[error("Internal error: {0}")]
    Internal(String),
}

/// Configuration related errors
#[derive(Error, Debug)]
pub enum ConfigError {
    #[error("Configuration file not found: {path}")]
    FileNotFound { path: String },
    
    #[error("Invalid configuration format: {message}")]
    InvalidFormat { message: String },
    
    #[error("Missing required configuration: {key}")]
    MissingRequired { key: String },
    
    #[error("Invalid configuration value for {key}: {value}")]
    InvalidValue { key: String, value: String },
}

/// Actor system related errors
#[derive(Error, Debug)]
pub enum ActorError {
    #[error("Actor not found: {actor_id}")]
    NotFound { actor_id: String },
    
    #[error("Actor communication timeout: {actor_id}")]
    Timeout { actor_id: String },
    
    #[error("Actor system initialization failed: {reason}")]
    InitializationFailed { reason: String },
    
    #[error("Message handling failed: {message}")]
    MessageHandlingFailed { message: String },
    
    #[error("Actor supervision failed: {reason}")]
    SupervisionFailed { reason: String },
}

/// Test execution related errors
#[derive(Error, Debug)]
pub enum TestExecutionError {
    #[error("Test case not found: {path}")]
    TestCaseNotFound { path: String },
    
    #[error("Invalid test case format: {reason}")]
    InvalidTestCase { reason: String },
    
    #[error("Test step execution failed: {step_name} - {reason}")]
    StepExecutionFailed { step_name: String, reason: String },
    
    #[error("Test assertion failed: {assertion} - expected: {expected}, actual: {actual}")]
    AssertionFailed { assertion: String, expected: String, actual: String },
    
    #[error("Test timeout: {timeout_ms}ms")]
    Timeout { timeout_ms: u64 },
    
    #[error("Variable not found: {variable_name}")]
    VariableNotFound { variable_name: String },
    
    #[error("Parameter iteration failed: {reason}")]
    ParameterIterationFailed { reason: String },
}

/// Plugin related errors
#[derive(Error, Debug)]
pub enum PluginError {
    #[error("Plugin not found: {plugin_name}")]
    NotFound { plugin_name: String },
    
    #[error("Plugin loading failed: {plugin_path} - {reason}")]
    LoadingFailed { plugin_path: String, reason: String },
    
    #[error("Plugin function not found: {function_name}")]
    FunctionNotFound { function_name: String },
    
    #[error("Plugin function execution failed: {function_name} - {reason}")]
    ExecutionFailed { function_name: String, reason: String },
    
    #[error("Plugin compilation failed: {reason}")]
    CompilationFailed { reason: String },
    
    #[error("Plugin version incompatible: required {required}, found {found}")]
    VersionIncompatible { required: String, found: String },
}

/// Storage related errors
#[derive(Error, Debug)]
pub enum StorageError {
    #[error("Database connection failed: {reason}")]
    ConnectionFailed { reason: String },
    
    #[error("Query execution failed: {query} - {reason}")]
    QueryFailed { query: String, reason: String },
    
    #[error("Transaction failed: {reason}")]
    TransactionFailed { reason: String },
    
    #[error("Cache operation failed: {operation} - {reason}")]
    CacheFailed { operation: String, reason: String },
    
    #[error("File operation failed: {path} - {reason}")]
    FileFailed { path: String, reason: String },
}

/// Network/HTTP related errors
#[derive(Error, Debug)]
pub enum NetworkError {
    #[error("HTTP request failed: {url} - {status}")]
    RequestFailed { url: String, status: u16 },
    
    #[error("Connection timeout: {url}")]
    Timeout { url: String },
    
    #[error("DNS resolution failed: {host}")]
    DnsResolutionFailed { host: String },
    
    #[error("SSL/TLS error: {reason}")]
    TlsError { reason: String },
    
    #[error("WebSocket error: {reason}")]
    WebSocketError { reason: String },
    
    #[error("Invalid URL: {url}")]
    InvalidUrl { url: String },
}

/// AI service related errors
#[derive(Error, Debug)]
pub enum AIError {
    #[error("AI service unavailable: {service}")]
    ServiceUnavailable { service: String },
    
    #[error("AI API error: {code} - {message}")]
    ApiError { code: String, message: String },
    
    #[error("AI model not found: {model}")]
    ModelNotFound { model: String },
    
    #[error("AI generation failed: {reason}")]
    GenerationFailed { reason: String },
    
    #[error("AI analysis failed: {reason}")]
    AnalysisFailed { reason: String },
    
    #[error("AI quota exceeded: {service}")]
    QuotaExceeded { service: String },
}

/// Validation related errors
#[derive(Error, Debug)]
pub enum ValidationError {
    #[error("Field validation failed: {field} - {reason}")]
    FieldValidation { field: String, reason: String },
    
    #[error("Schema validation failed: {reason}")]
    SchemaValidation { reason: String },
    
    #[error("Type validation failed: expected {expected}, found {found}")]
    TypeValidation { expected: String, found: String },
    
    #[error("Range validation failed: {field} must be between {min} and {max}")]
    RangeValidation { field: String, min: String, max: String },

    #[error("Invalid pattern: {pattern} - {message}")]
    InvalidPattern { pattern: String, message: String },
}

/// Serialization/Deserialization related errors
#[derive(Error, Debug)]
pub enum SerializationError {
    #[error("JSON serialization failed: {reason}")]
    JsonSerialization { reason: String },
    
    #[error("YAML serialization failed: {reason}")]
    YamlSerialization { reason: String },
    
    #[error("TOML serialization failed: {reason}")]
    TomlSerialization { reason: String },
    
    #[error("Binary serialization failed: {reason}")]
    BinarySerialization { reason: String },
}

/// Result type alias for the platform
pub type Result<T> = std::result::Result<T, Error>;

/// Helper trait for converting errors with context
pub trait ErrorContext<T> {
    fn with_context<F>(self, f: F) -> Result<T>
    where
        F: FnOnce() -> String;
}

impl<T, E> ErrorContext<T> for std::result::Result<T, E>
where
    E: Into<Error>,
{
    fn with_context<F>(self, f: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|e| {
            let context = f();
            Error::Internal(format!("{}: {}", context, e.into()))
        })
    }
}

/// Helper function to create internal errors
pub fn internal_error<T: fmt::Display>(message: T) -> Error {
    Error::Internal(message.to_string())
}

/// Helper function to create configuration errors
pub fn config_error<T: fmt::Display>(message: T) -> Error {
    Error::Config(ConfigError::InvalidFormat {
        message: message.to_string(),
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_display() {
        let error = Error::Config(ConfigError::FileNotFound {
            path: "/path/to/config".to_string(),
        });
        
        assert!(error.to_string().contains("Configuration file not found"));
    }

    #[test]
    fn test_error_context() {
        let result: std::result::Result<(), std::io::Error> = Err(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            "file not found",
        ));

        let error = result.with_context(|| "Failed to read configuration".to_string());
        assert!(error.is_err());
    }
}

impl From<serde_json::Error> for Error {
    fn from(err: serde_json::Error) -> Self {
        Error::Serialization(SerializationError::JsonSerialization {
            reason: err.to_string(),
        })
    }
}

/// Database-specific errors
#[derive(Debug, thiserror::Error)]
pub enum DatabaseError {
    #[error("Connection failed: {message}")]
    ConnectionFailed { message: String },

    #[error("Migration failed: {message}")]
    MigrationFailed { message: String },

    #[error("Query failed: {message}")]
    QueryFailed { message: String },

    #[error("Serialization failed: {message}")]
    SerializationFailed { message: String },

    #[error("Deserialization failed: {message}")]
    DeserializationFailed { message: String },

    #[error("Entity not found: {entity} with id {id}")]
    NotFound { entity: String, id: String },

    #[error("Constraint violation: {message}")]
    ConstraintViolation { message: String },

    #[error("Transaction failed: {message}")]
    TransactionFailed { message: String },
}

/// Cache-specific errors
#[derive(Debug, thiserror::Error)]
pub enum CacheError {
    #[error("Connection failed: {message}")]
    ConnectionFailed { message: String },

    #[error("Operation failed: {operation} - {message}")]
    OperationFailed { operation: String, message: String },

    #[error("Serialization failed: {message}")]
    SerializationFailed { message: String },

    #[error("Deserialization failed: {message}")]
    DeserializationFailed { message: String },

    #[error("Key not found: {key}")]
    KeyNotFound { key: String },

    #[error("TTL expired for key: {key}")]
    TtlExpired { key: String },
}
